﻿using System.Reflection;

namespace RulesEngine
{
    public class RuleUtility
    {
        public static void SetPropertyValue(object instance, string propertyName, string propertyValue)
        {
            if (instance == null) throw new ArgumentNullException("instance");
            if (string.IsNullOrEmpty(propertyName)) throw new ArgumentException("Property name cannot be null or zero-length.", "propertyName");

            Type type = instance.GetType();
            PropertyInfo property;
            property = type.GetProperty(propertyName);
            if (property == null)
            {
                string msg = string.Format("Property '{0}' not found on object '{1}'", propertyName, instance);
                throw new ApplicationException(msg);
            }

            object convertedValue;
            if (property.PropertyType.IsEnum)
            {
                convertedValue = Enum.Parse(property.PropertyType, propertyValue);
            }
            else if (property.PropertyType.IsGenericType && Nullable.GetUnderlyingType(property.PropertyType) != null && Nullable.GetUnderlyingType(property.PropertyType).IsEnum)
            {
                convertedValue = string.IsNullOrWhiteSpace(propertyValue) ? null : Enum.Parse(Nullable.GetUnderlyingType(property.PropertyType), propertyValue);
            }
            else
            {
                //Type propertyType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
                //convertedValue = System.Convert.ChangeType(propertyValue, propertyType);

                //convertedValue = System.Convert.ChangeType(propertyValue, property.PropertyType);
                convertedValue = ChangeType(propertyValue, property.PropertyType);
            }

            property.SetValue(instance, convertedValue, null);
        }

        public static object ChangeType(object value, Type conversion)
        {
            var t = conversion;

            if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
            {
                if (value == null)
                {
                    return null;
                }

                t = Nullable.GetUnderlyingType(t);
            }

            return Convert.ChangeType(value, t);
        }

        public static void ValidateRuleTypeExactMatch<TCandidate>(Type ruleType)
        {
            if (!(Activator.CreateInstance(ruleType) is IRule<TCandidate>))
                throw new ApplicationException(string.Format("Rule type [{0}] is not an exact match.  Type does not implement IRule<{1}>", ruleType.Name, typeof(TCandidate).ToString()));
        }
    }
}